/*****************************************************************
Copyright (c) 2008 Smart Mai (smardio@gmail.com).
All rights reserved. This program and the accompanying materials
are made available under the terms of the GNU Public License v2.0
which accompanies this distribution, and is available at
http://www.gnu.org/licenses/old-licenses/gpl-2.0.html

Contributors:
    Lin Haiwei - initial API and implementation
******************************************************************/
 
package com.smartdio.example.util;

import java.util.*;
import java.util.regex.*;

/*
 * 创建日期 2004-5-2
 * 
 */

/**
 * @author Lin Haiwei
 * 
 * 对字符进行过滤转换，包括：
 * 过滤转换HTML编码；
 * 过滤转换UBB代码；
 * 过滤转换非法字符或者词汇；
 * 过滤转换表情代码；
 * 
 * 其他功能：
 * 判断一个字符串是否是纯英文字符串；
 * 判断一个字符串是否是纯数字字符串。
 * 
 * 使用例子：
 *  	aFilter.setString(allStr);  //设置需要转换的字符串
 * 		aFilter.setIllegalMap(illegalStrs);  //设置非法词汇表
 * 		aFilter.setExpressionMap(getExpressions());  //设置表情符号表
 * //设置需要执行的操作
 * 								aFilter.setHtmlFiltrate(true);
 * 								aFilter.setIllegalFiltrate(true);
 * 								aFilter.setExpressionFiltrate(true);
 * 								aFilter.setUbbFiltrate(true);
 * 								String finalStr = aFilter.getFinalString(); //得到最终过滤转换的字符串。
 *
 */
public class TextFilter {
// 属性设置
	private boolean illegalFiltrate; //非法字符检查
	private boolean htmlFiltrate; //Html检查
	private boolean ubbFiltrate; //UBB检查
	private boolean expressionFiltrate; //表情检查

	// 过滤表
	private String[] illegalMap; //字符过滤表
	private Map ExpressionMap; //表情对照表

	// 数据源
	private static String strSource; //被过滤数据

	/**
	 * 设置执行表情代码转换
	 * @param b 如果是false，不执行表情代码转换。默认不执行
	 */
	public void setExpressionFiltrate(boolean b) {
		expressionFiltrate = b;
	}

	/**
	 * 设置执行HTML编码过滤
	 * @param b 如果是false，不执行HTML编码过滤。默认不执行
	 */
	public void setHtmlFiltrate(boolean b) {
		htmlFiltrate = b;
	}

	/**
	 * 设置执行非法字符过滤
	 * @param b 如果是false，不执行非法字符过滤。默认不执行
	 */
	public void setIllegalFiltrate(boolean b) {
		illegalFiltrate = b;
	}

	/**
	 * 设置执行UBB过滤
	 * @param b 如果是false，不执行。默认不执行
	 */
	public void setUbbFiltrate(boolean b) {
		ubbFiltrate = b;
	}

	/**
	 * 设置需要转换和过滤的字符串。
	 * @param str  字符串
	 * 
	 */
	public void setString(String str) {
		strSource = str;
	}

	/**
	 *  设置表情符号表
	 * @param map  一个Map对象。key是表情符号，值是需要转换成的连接或者编码。
	 */
	public void setExpressionMap(Map map) {
		ExpressionMap = map;
	}
  /**
   * 替换文本中的HTML标记，包括：
   * replaceStr(strSource, "&amp;", "&amp;amp;"); //替换amp
   * replaceStr(strSource, "&gt;", "&amp;gt;"); //替换"&gt;
   * replaceStr(strSource, "&lt;", "&amp;lt;"); //替换"&lt;"
   * replaceStr(strSource, "&nbsp;&nbsp;", "&amp;nbsp;&amp;nbsp;"); //替换空格
   * replaceStr(strSource, "&#39;", "&amp;#39;"); //替换单引号
   * replaceStr(strSource, "&quot;", "&amp;quot;"); //替换双引号
   * replaceStr(strSource, "\n", "&lt;br&gt;"); //替换换行符
   */
	private void convertHtml() {
		if (strSource == null)
			throw new NullPointerException();

		strSource = replaceStr(strSource, "&", "&amp;"); //替换amp
		strSource = replaceStr(strSource, ">", "&gt;"); //替换">
		strSource = replaceStr(strSource, "<", "&lt;"); //替换"<"
		strSource = replaceStr(strSource, "  ", "&nbsp;&nbsp;"); //替换空格
		strSource = replaceStr(strSource, "'", "&#39;"); //替换单引号
		strSource = replaceStr(strSource, "\"", "&quot;"); //替换双引号
		strSource = replaceStr(strSource, "\n", "<br>"); //替换换行符
		//strSource = replaceStr(strSource, "\r", "<p></p>"); //替换回车
	}
	
	static public String convertHtml(String strSource){
		strSource = replaceStr(strSource, "&", "&amp;"); //替换amp
		strSource = replaceStr(strSource, ">", "&gt;"); //替换">
		strSource = replaceStr(strSource, "<", "&lt;"); //替换"<"
		strSource = replaceStr(strSource, "  ", "&nbsp;&nbsp;"); //替换空格
		strSource = replaceStr(strSource, "'", "&#39;"); //替换单引号
		strSource = replaceStr(strSource, "\"", "&quot;"); //替换双引号
		strSource = replaceStr(strSource, "\n", "<br>"); //替换换行符
		//strSource = replaceStr(strSource, "\r", "<p></p>"); //替换回车
		return strSource;
	}

  /**
   * 过滤文本中的非法字符。
   * 非法字符需要由外部程序定义。设定非法字符表的方法是：setIllegalMap(String[] strings)
   * 例如：
   * String[] illegalStrs =new String[]{"妈的","去你的"};
   * String illegalStr ="今天是妈妈的生日，我们去你的家里吃饭庆祝吧？";
   * aFilter.setString(illegalStr);
   * aFilter.setIllegalMap(illegalStrs);
   * 						aFilter.setHtmlFiltrate(false);
   * 						aFilter.setIllegalFiltrate(true);
   * 						aFilter.setExpressionFiltrate(false);
   * 						aFilter.setUbbFiltrate(false);
   * 						String finalStr = aFilter.getFinalString();
   * 得到的finalStr 是"今天是妈生日，我们家里吃饭庆祝吧？";
   *
   */
	private void convertIllegal() {
		if (strSource == null || illegalMap == null)
			throw new NullPointerException();

		for (int i = 0; i < illegalMap.length; i++) {
			strSource = replaceStr(strSource, illegalMap[i], "");
		}

	}
	
	
 /**
  * 把文本中的UBB代码转换成对应的HTML代码
  * 例如：
  * String ubbString="hello,你好[img]http://www.smartdio.com/a.jpg[/img]" +
  	"[url]http://www.smartdio.com/a.jpg[/url]";
  *
  * aFilter.setString(ubbString);
  *   	aFilter.setHtmlFiltrate(false);
  *   	aFilter.setIllegalFiltrate(false);
  *   	aFilter.setExpressionFiltrate(false);
  *   	aFilter.setUbbFiltrate(true);
  *   	String finalStr = aFilter.getFinalString();
  * 得到的结果是："hello,你好<img src=\"http://www.smartdio.com/a.jpg\">" +
  	"<a href=\"http://www.smartdio.com/a.jpg\" target=\"_blank\">http://www.smartdio.com/a.jpg</a>" 。
  */
	private void convertUBB() {
		if (strSource == null)
			throw new NullPointerException();

		strSource =
			convertTags(
				strSource,
				"\\[(img|IMG)\\](HTTP|http)://([^\\[]+)(gif|GIF|JPG|jpg|PNG|png)\\[/(img|IMG)\\]",
				"<img src=\"$2://$3$4\">");
		//图片符号 格式: [img]String[/img] [IMG]String[/IMG]
		//检查标准：1. 使用http服务 2. 格式中不包含符号"[" 3.以gif|jpg|png结尾 (允许的图片格式)

		strSource =
			convertTags(
				strSource,
				"\\[(url|URL)\\](HTTP|http|ftp|FTP)://([^\\[]+)\\[/(url|URL)\\]",
				"<a href=\"$2://$3\" target=\"_blank\">$2://$3</a>");
		//连接 格式: [url]String[/url] [URL]String[/URL]
		//检查标准：1. 使用http或ftp服务 2. 格式中不包含"["
		strSource =
			convertTags(
				strSource,
				"\\[(url|URL)=(HTTP|http|ftp|FTP)://([^\\[]+)\\](.+)\\[/(url|URL)\\]",
				"<a href=\"$2://$3\" target=\"_blank\">$4</a>");
		//自定义连接 格式: [url=http://xxx.com]String[/url] [URL=http://xxx.com]String[/URL]
		//检查标准: 1. 使用http或ftp服务	2.地址中不能包含符号"["
		strSource =
			convertTags(
				strSource,
				"\\[(email|EMAIL)\\]([^\\[|^@|^#|^%|^&|^*|^~|^\\^|^!|^$|^\\(|^\\)|^\\\\]{2,20})@([^\\[]{3,30})\\[/(email|EMAIL)\\]",
				"<a href=mailto:$2@$3>$2@$3</a>");
		//email连接符号 格式: [email]haiweix@msn.com[/email] [EMAIL]haiweix@msn.com[/EMAIL]
		//检查标准: 1. 非~!@#$%^&*()\字符2-20个 2. 包含@符号 3.非"["符号3-30个
		strSource =
			convertTags(
				strSource,
				"\\[(email|EMAIL)=([a-zA-Z0-9|_|\\.]{2,20})@([a-zA-Z0-9|_|\\.]{3,30})\\](w+)\\[/(email|EMAIL)\\]",
				"<a href=mailto:$2@$3>$4</a>");
		//自定义email连接符号 格式: [email=haiweix@msn.com]String[/email] [EMAIL=haiweix@msn.com]String[/EMAIL]
		//检查标准: email (1. 开头字符a-zA-Z0-9或"_","."字符2-20个 2. 结尾字符a-zA-Z0-9或"_","."字符3-30个 ) String(a-zA-z0-9字符)
		strSource =
			convertTags(
				strSource,
				"\\[(code|CODE)\\](.+)\\[/(code|CODE)\\]",
				"<div style=\"margin:20px; margin-top:5px\"><div style=\"margin-bottom:2px\">代码:</div>	" +
				"	<pre class=\"alt2\" style=\"margin:0px; padding:6px; border:1px inset; width:640px; height:34px;" +
				"overflow:auto\">$2</pre></div>");
		//代码符号 格式: [code]String[/code] [CODE]String[/CODE]
		//检查标准: 符合格式的任意字符
		strSource =
			convertTags(
				strSource,
				"\\[(quote|QUOTE)\\](.+)\\[/(quote|QUOTE)\\]",
				"<div style=\"margin-bottom:2px\">引用:</div><table cellpadding=\"6\" " +
				"cellspacing=\"0\" border=\"0\" width=\"100%\"><tr><td style=\"border:1px " +
				"inset\"> $2 </td></tr></table></div>");
		//引用符号 格式: [quote]String[/quote] [QUOTE]String[/QUOTE]
		//检查标准： 符合格式的任意字符
		strSource =
			convertTags(strSource, "\\[(b|B)\\](.+)\\[/(b|B)\\]", "<b>$2</b>");
		//粗体效果 格式: [b]String[/b] [B]String[/B]
		//检查标准： 符合格式的任意字符
		strSource =
			convertTags(strSource, "\\[(i|I)\\](.+)\\[/(i|I)\\]", "<i>$2</i>");
		//倾斜效果 格式: [i]String[/i] [I]String[/I]
		//检查标准： 符合格式的任意字符

		strSource =
			convertTags(strSource, "\\[(u|U)\\](.+)\\[/(u|U)\\]", "<u>$2</u>");
		//下划线效果 格式: [u]String[/u] [U]String[/U]
		//检查标准： 符合格式的任意字符

		strSource =
			convertTags(strSource, "\\[(s|S)\\](.+)\\[/(s|S)\\]", "<s>$2</s>");
		//删除线 格式: [s]String[/s] [S]String[/S]
		//检查标准： 符合格式的任意字符
		strSource =
			convertTags(
				strSource,
				"\\[(sup|SUP)\\](.+)\\[/(sup|SUP)\\]",
				"<sup>$2</sup>");
		//上标文字 格式: [sup]String[/sup] [SUP]String[/SUP]
		//检查标准： 符合格式的任意字符
		strSource =
			convertTags(
				strSource,
				"\\[(sub|SUB)\\](.+)\\[/(sub|SUB)\\]",
				"<sub>$2</sub>");
		//下标文字 格式: [sub]String[/sub] [SUB]String[/SUB]
		//检查标准： 符合格式的任意字符

		strSource =
			convertTags(
				strSource,
				"\\[(font|FONT)=([^\\[]{3,16})\\](.+)\\[/(font|FONT)\\]",
				"<font face=$2>$3</font>");
		//文字字体 格式: [font=宋体]改变字体[/font] [FONT=宋体]改变字体[/FONT]
		//检查标准: 字体名称(1.不包含字符"[" 2.长度为3-6个字符) 控制效用范围内字符不检查

		strSource =
			convertTags(
				strSource,
				"\\[(size|SIZE)=([0-9|\\.]{1,4}c?m?|p?t?)\\](.+)\\[/(size|SIZE)\\]",
				"<font size=$2>$3</font>");
		//文字大小 格式: [size=4]改变文字大小[/size] [SIZE=4]改变文字大小[/SIZE]
		//检查标准: 字体大小(1. 0-9范围内包含字符"." 2.长度为1-4个数字 如10.5 3,大小控制表示cm或pt) 控制效用范围内字符不检查

		strSource =
			convertTags(
				strSource,
				"\\[(color|COLOR)=(#?[0-9]{6,6}|[a-zA-Z]{3,10})\\](.+)\\[/(color|COLOR)\\]",
				"<font color=$2>$3</font>");
		//文字颜色 格式: [color=red]改变文字颜色[/color] [COLOR=RED]改变文字颜色[/COLOR]
		//检查标准: 字体颜色 (1, 6位RGB代码 #000000 2, 色彩单词 单词长度为3-10) 控制效用范围内字符不检查
		strSource =
			convertTags(
				strSource,
				"\\[(fly|FLY)\\](.+)\\[/(fly|FLY)\\]",
				"<MARQUEE scrollAmount=3 behavior=alternate width=\"90%\">$2</MARQUEE>");
		//飞行文字 格式: [fly]飞行文字特效[/fly] [FLY]飞行文字特效[/FLY]
		//检查标准： 符合格式的任意字符
		strSource =
			convertTags(
				strSource,
				"\\[(move|MOVE)\\](.+)\\[/(move|MOVE)\\]",
				"<MARQUEE scrollAmount=3 width=\"90%\">$2</MARQUEE>");
		//滚动文字 格式：[move]滚动文字特效[/move] [MOVE]滚动文字特效[/MOVE]
		//检查标准: 符合格式的任意字符
		strSource =
			convertTags(
				strSource,
				"\\[(swf|SWF)\\](HTTP|http)://([^\\[]+)(swf|SWF)\\[/(swf|SWF)\\]",
				"<PARAM NAME=PLAY VALUE=TRUE><PARAM NAME=LOOP VALUE=TRUE><PARAM " +
				"NAME=QUALITY VALUE=HIGH><embed src=http://$3$4 quality=high " +
				"pluginspage=\"http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash\" " +
				"type=\"application/x-shockwave-flash\" WIDTH=600 height=500></embed>");
		//SWF文件 格式: [swf]http://www.micromedia.com/demo.swf[/swf] [SWF]http://www.micromedia.com/demo.swf[/SWF]
		//检查标准 1. http服务方式 2.地址文字不包含"["字符 2.插入文件为swf文件
		strSource =
			convertTags(
				strSource,
				"\\[(swf|SWF)=([0-9]{1,3})([0-9]{1,3})\\](HTTP|http)://([^\\[]+)(swf|SWF)\\[/(swf|SWF)\\]",
								"<PARAM NAME=PLAY VALUE=TRUE><PARAM NAME=LOOP VALUE=TRUE><PARAM " +
								"NAME=QUALITY VALUE=HIGH><embed src=http://$5$6 quality=high " +
								"pluginspage=\"http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash\" " +
								"type=\"application/x-shockwave-flash\" WIDTH=$2 height=$3></embed>");
		//自定义大小SWF文件 格式: [swf]http://www.micromedia.com/demo.swf[/swf] [SWF]http://www.micromedia.com/demo.swf[/SWF]
		//检查标准 1. 大小为0-9之间的三位数字 2. http服务方式 3.地址文字不包含"["字符 4.插入文件为swf文件



		//列表标签 格式:[list][*]aaa[*]bbb[/list] [LIST][*]aaa[*]bbb[/LIST]

	}
  /**
   * 把文本中的表情符号转换称为对应的图片连接或者其他字符。
   * 需要由外部程序指定每个表情符号对应的图片连接地址或者替换的内容，方法是 setExpressionMap(Map map) 
   *
   */
	private void convertExpression() {
		if (strSource == null || ExpressionMap.isEmpty())
			throw new NullPointerException();

		Iterator it = ExpressionMap.keySet().iterator();
		while (it.hasNext()) {
			String strFrom = it.next().toString();
			String strTo = ExpressionMap.get(strFrom).toString();
			strSource = replaceStr(strSource, strFrom, strTo);
		}

	}
  
  /**
   * 判断字符串是否是纯英文（包括数字）。
   * @return  如果不是英文，包含了中文或者其他字符，将返回false.
   */
	public boolean isEnglishOnly() { //判断是否为英文
		if (strSource == null)
			throw new NullPointerException();

		Pattern p = Pattern.compile("([^a-zA-Z0-9|^-|^_]+)");
		//标准: 包含英文大小写字符，数字和特殊符号"-"和"_"
		Matcher m = p.matcher(strSource);
		return m.find() ? false : true;
	}

  /**
   * 判断是否是纯数字。
   * @return  如果包含了英文字母或者标点符号、中文字符，将返回false。可以包含小数点和逗号
   */
	public boolean isNumberOnly() { //判断是否为数字
		if (strSource == null)
			throw new NullPointerException();
		Pattern p = Pattern.compile("([^0-9|^\\.|^,]+)"); //标准: 0-9之间,含"."和","

		Matcher m = p.matcher(strSource);
		return m.find() ? false : true;
	}

  /**
   * 获取经过转换后的内容。
   * @return
   */
	public String getFinalString() {
		if (illegalFiltrate)
			convertIllegal();
		if (htmlFiltrate)
			convertHtml();
		if (ubbFiltrate)
			convertUBB();
		if (expressionFiltrate)
			convertExpression();

		return strSource;
	}

	/**
	 * 设置非法字符表
	 * @param strings
	 */
	public void setIllegalMap(String[] strings) {
		illegalMap = strings;
	}

	static protected String replaceStr(String content, String from, String to) {
		String strData = "";
		String strTemp = content;
		while ((strTemp.indexOf(from) >= 0) && (!from.equals(""))) {
			int start = strTemp.indexOf(from);
			int end = start + from.length();
			strData += strTemp.substring(0, start) + to;
			strTemp = strTemp.substring(end, strTemp.length());
		}
		return strData + strTemp;
	}

  
	protected static String convertTags(
		String strContent,
		String strTag,
		String strTo) {
		if (strContent == null || strContent.length() == 0) {
			return strContent;
		}
		Pattern p = Pattern.compile(strTag);
		Matcher m = p.matcher(strContent);
		StringBuffer sb = new StringBuffer();
		boolean result = m.find();
		while (result) {
			m.appendReplacement(sb, strTo);
			result = m.find();
		}
		m.appendTail(sb);
		return sb.toString();
	}
}
